#include <stdio.h>
#include <string.h>
#include <memory>
#include <thread>
//#include "Util/logc.h"
#include "RealStreamSource.h"
#include "RtspClientStream.h"
#include "AudioEncode.h"
#include "VideoEncode.h"
#include "Pal/VideoEnc.h"
#include "Pal/AudioEnc.h"
#include "Util/util.h"
// #include "Parser/Frame.h"
#include "Thread/Thread.h"
#include "mediaPriv.h"

using namespace Fdt;
using namespace FrameCommon;

#define AUDIO_CAP

/// 取单件实例
//RealStreamSource::Ptr
//std::shared_ptr<RealStreamSource>
static bool verify_channel_stream(int video, int audio)
{
	return true;
}

namespace Media{

std::recursive_mutex 				RealStreamSource::s_stream_mutex;
RealStreamSource::StreamMap			RealStreamSource::s_streamMap;

RealStreamSource::Ptr RealStreamSource::create(int video, int audio)
{
    tracef("RealStreamSource enter, video %d, audio %d!", video, audio);

	if(video == RTSP_MAIN_OUT || video == RTSP_SUB_OUT1
		|| video == RTSP_SUB_OUT2 || video == RTSP_SUB_OUT3
		|| video == RTSP_SUB_OUT4 || video == RTSP_SUB_OUT5
		|| video == RTSP_SUB_OUT6 || video == RTSP_SUB_OUT7
		|| video == RTSP_SUB_OUT8){
			audio = video;
	}else{
		audio = RTSP_MAIN_OUT;
		video = RTSP_MAIN_OUT;
	}
    
	tracef("RealStreamSource create, video %d, \
	audio %d", video, audio);

	std::lock_guard<std::recursive_mutex> guard(s_stream_mutex);
	if(!verify_channel_stream(video, audio))
	{
		return nullptr;
	}
	SourceKey key(video, audio);
	if(s_streamMap.find(key) != s_streamMap.end()){
		auto ptr = s_streamMap[key].lock();
		if(!ptr){
			s_streamMap.erase(key);
		}
		else{
			return ptr;
		}
	}
	auto ptr = std::make_shared<RealStreamSource>(video, audio);
	s_streamMap[key] = ptr;
	return ptr;
}

void RealStreamSource::destroy()
{
    std::lock_guard<std::recursive_mutex> lock(s_stream_mutex);
    SourceKey key(_video, _audio);
    if(s_streamMap.find(key) != s_streamMap.end())
    	s_streamMap.erase(key);

}

RealStreamSource::RealStreamSource(int video, int audio)
{
	_videoenc = VideoEncode::instance(video);
	_audioenc = AudioEncode::instance(audio);

	auto ad = _audioenc ? true : false;
	auto vd = _videoenc ? true : false;

	_video = video;
	_audio = audio;
}

RealStreamSource::~RealStreamSource()
{
	//tracef("RealStreamSourceAA destructor");
	std::lock_guard<std::recursive_mutex> lock(s_stream_mutex);//一进来就加锁
	clear();
	//注意RAII方式带来的问题
	//理论上对象析构时,会释放所有资源,包括线程对象,线程对象的释放可以做到等待线程终止时再释放
	//但是!!!!!!!!!!!!!!!!!!!!
	//对于线程对象有一个额外需要注意的点,就是thread对象析构之前,会等待线程退出,线程退出之前有可能访问其它成员对象,但是其他成员对象可能先于thread对象析构
	//所以一定保证线程最先释放,比如在析构中手动释放thread对象
	//或者保证线程不能再访问其其他成员对象

	destroy();
}


/// 取帧数据回调函数
void RealStreamSource::onVideoPacket(const FrameCommon::Frame::Ptr &frame)
{
    _video_mutex.lock();
    auto func_map = _video_func_map;
    _video_mutex.unlock();

	for(auto &func : func_map){
		(*(func.second))(frame);
	}
}

void RealStreamSource::onAudioPacket(const FrameCommon::Frame::Ptr &frame)
{
    _audio_mutex.lock();
    auto func_map = _audio_func_map;
    _audio_mutex.unlock();

	for(auto &func : func_map){
		(*(func.second))(frame);
	}
}

/// 绑定回调,开始取流
bool RealStreamSource::start(void *thiz, const CallBack &callback)
{
	bool video = true;
	bool audio = true;


	{
		std::lock_guard<std::recursive_mutex> guard(_video_mutex);
		if(_video_func_map.find(thiz) != _video_func_map.end()){
		}
		else{
			_video_func_map[thiz] = std::make_shared<CallBack>(callback);
		}

		if(_video_func_map.size() == 1){
			//if channel is RTSP_SUB_OUT4~7, direct to get frame

			 if(_video == RTSP_SUB_OUT4 || _video == RTSP_SUB_OUT5
         		|| _video == RTSP_SUB_OUT6 || _video == RTSP_SUB_OUT7){
				auto rtsp_stream = RtspClientStream::GetInstance(_video);
				if(rtsp_stream){
					std::weak_ptr<RealStreamSource> weakSelf = shared_from_this();
					rtsp_stream->SetDirectFrameCallBack( [weakSelf](const Frame::Ptr &framePtr){
						auto strongSelf = weakSelf.lock();
						if (!strongSelf) {
							return;
						}
						strongSelf->onVideoPacket(framePtr);
					});
				}
		 	}else if(_videoenc){
			    setVideoFormat();//设置视频格式
			    
				std::weak_ptr<RealStreamSource> weakSelf = shared_from_this();
				video = _videoenc->start(this, [weakSelf](const Frame::Ptr &framePtr){
			        auto strongSelf = weakSelf.lock();
			        if (!strongSelf) {
			            return;
			        }
			        strongSelf->onVideoPacket(framePtr);
				});
			}
		}
	}
#ifdef AUDIO_CAP	
	{
		std::lock_guard<std::recursive_mutex> guard(_audio_mutex);
		if(_audio_func_map.find(thiz) != _audio_func_map.end()){
		}
		else{
			_audio_func_map[thiz] = std::make_shared<CallBack>(callback);;
		}
		if(_audio_func_map.size() == 1){
			tracef("_video_mutex_leave");
			if(_audioenc){
			    setAudioFormat(); //设置音频格式
			    
				std::weak_ptr<RealStreamSource> weakSelf = shared_from_this();
				audio = _audioenc->start(this, [weakSelf](const Frame::Ptr &framePtr){
			        auto strongSelf = weakSelf.lock();
			        if (!strongSelf) {
			            return;
			        }
			        strongSelf->onAudioPacket(framePtr);
				});
			}
		}
	}
#endif
	if(video || audio){
//		mediaCtl_g.ndiState = 1;
//		MEDIA_NdiSetConnectStatus(1);
	}
	return (video && audio);
}


/// 解除绑定,停止取流
bool RealStreamSource::stop(void *thiz)
{
	bool video = false;
	bool audio = false;
	{
		std::lock_guard<std::recursive_mutex> guard(_video_mutex);
		if(_video_func_map.find(thiz) != _video_func_map.end()){
			_video_func_map.erase(thiz);
			if(_video_func_map.empty()){
				video = _videoenc ? _videoenc->stop(this):true;
			}
		}
		else{
			video = true;
		}
	}
#ifdef AUDIO_CAP
	{
		std::lock_guard<std::recursive_mutex> guard(_audio_mutex);
		if(_audio_func_map.find(thiz) != _audio_func_map.end()){
			_audio_func_map.erase(thiz);
			if(_audio_func_map.empty()){
				audio = _audioenc ? _audioenc->stop(this):true;
			}
		}
		else{
			audio = true;
		}
	}
#endif
//	mediaCtl_g.ndiState = 0;
//	MEDIA_NdiSetConnectStatus(0);
	return (video && audio);
}

/// 解除绑定,停止取流
bool RealStreamSource::clear()
{
	{
		std::lock_guard<std::recursive_mutex> guard(_video_mutex);
		_video_func_map.clear();
		if(_videoenc){
			_videoenc->stop(this);
		}
	}
#ifdef AUDIO_CAP
	{
		std::lock_guard<std::recursive_mutex> guard(_audio_mutex);
		_audio_func_map.clear();
		if(_audioenc){
			_audioenc->stop(this);
		}
	}
#endif
	return true;
}

bool RealStreamSource::setIFrame()
{
	return _videoenc ? _videoenc->setIFrame() : false;
}



static void convFormat(VideoFormat *format, const VideoEncFormat *EncFormat){
	format->type     = 0;
	format->BitRate  = EncFormat->bitrate;
	format->Width    = EncFormat->width;
	format->Height   = EncFormat->height;
	format->Fps      = (int)(EncFormat->fps);
	format->GOP      = EncFormat->gop;
}

bool RealStreamSource::getVideoFormat(VideoFormat *format){

	if(!format || !_videoenc)
		return false;

	VideoEncFormat EncFormat{};
	if(!_videoenc->getFormat(&EncFormat))
		return false;

	convFormat(format, &EncFormat);
	return  true;
}


bool RealStreamSource::setVideoFormat(){
    if(!_videoenc)
    return false;
    
	//setFormat        
    VideoEncFormat encFormat;
    memset(&encFormat, 0, sizeof(VideoEncFormat));
    encFormat.type = videoEncH264; //videoEncH265
	

	encFormat.width = 1920;
    encFormat.height = 1080;
	if(_video == RTSP_SUB_OUT1 || _video == RTSP_SUB_OUT3){
		encFormat.width = 640;
		encFormat.height = 360;
	}
    
    encFormat.bitrateControl = videoEncBitrateCtrlConstant;  
    encFormat.fps = 30;
    encFormat.gop = encFormat.fps*2;
    encFormat.bitrate = (1*encFormat.width*encFormat.height/1024)*encFormat.fps/30;
    encFormat.quality = 1;
    encFormat.qualityType = 0;
    encFormat.pack = videoEncPackTypeRAW;
    encFormat.profile = h264ProfileHigh;

    bool result = _videoenc->setFormat(&encFormat);
    if(!result)
    {
		errorf("Video setFormat failed");
		return false;
	}
	
	return  true;
}

bool RealStreamSource::setAudioFormat(){

    if(!_audioenc)
    return false;
    
	AudioEncFormat encFormat;
    memset(&encFormat, 0, sizeof(encFormat));
	encFormat.type = audioEncAAC;
    encFormat.pack = audioEncPackTypeADTS;
    encFormat.frequency = 48000; //AAC要求输入输出采样率一致
    encFormat.channelNum = 2;
    encFormat.depth = 16;
    
    bool result = _audioenc->setFormat(&encFormat);
    if(!result)
    {
		errorf("Video setFormat failed");
		return false;
	}
	
	return  true;
}


}//namespace Media



